package server

import (
	"chitchat_http/model"
	"chitchat_http/util"
	"encoding/json"
	"fmt"
	"github.com/doujunyu/gogo/gogo"
	"github.com/doujunyu/gogo/gogo_log"
	"github.com/doujunyu/gogo/sql_aid"
	"time"
)

const TcpListChannel = "tcp_list_channel"

const MessageByExit = "MessageByExit"                                                   //退出
const MessageByMustExit = "MessageByMustExit"                                           //强制退出
const MessageByLogin = "MessageByLogin"                                                 //登录
const MessageByOnlyChat = "MessageByOnlyChat"                                           //单聊
const MessageByGroupChat = "MessageByGroupChat"                                         //群聊
const MessageByApplyFriend = "MessageByApplyFriend"                                     //申请加好友
const MessageByApplyFriendCheckAgree = "MessageByApplyFriendCheckAgree"                 //好友同意
const MessageByApplyFriendCheckRefused = "MessageByApplyFriendCheckRefused"             //好友拒绝
const MessageByApplyGroup = "MessageByApplyGroup"                                       //申请进组
const MessageByApplyGroupCheckAgree = "MessageByApplyFriendCheckAgree"                  //进组同意
const MessageByApplyGroupCheckRefused = "MessageByApplyFriendCheckRefused"              //进组拒绝
const MessageByApplyGroupReverse = "MessageByApplyGroupReverse"                         //邀请用户进组
const MessageByApplyGroupReverseCheckAgree = "MessageByApplyGroupReverseCheckAgree"     //邀请用户进组同意
const MessageByApplyGroupReverseCheckRefused = "MessageByApplyGroupReverseCheckRefused" //邀请用户进组拒绝
const MessageByApplyUserMessageClean = "MessageByApplyUserMessageClean"                 //消息数量消除

type MessageByStructMain struct {
	CmdMessage     string                 `json:"cmd_message"`      //消息类型
	ReceiveUserIds []interface{}          `json:"receive_user_ids"` //通知用户ID
	SendUserID     int64                  `json:"send_user_id"`     //发起的用户id,0为系统信息
	SendUser       interface{}            `json:"send_user_title"`  //发起的用户信息
	Content        map[string]interface{} `json:"content"`          //消息体
}

const MessageContentByGroupId = "group_id"       //组ID
const MessageContentByEquipment = "equipment"    //设备号
const MessageContentByPortIp = "port_ip"         //链接端口
const MessageContentByLinkServer = "link_server" //链接服务器
const MessageContentByContent = "content"        //内容
const MessageContentByToken = "token"            //token

// TcpWorkByPull 处理聊天队列中的数据
func TcpWorkByPull() {
	var redisData interface{}
	var dataSlice []interface{}
	var err error
	var data MessageByStructMain
	for true {
		if gogo.ServerStatus != gogo.ServerStatusAllow {
			break
		}
		redisData, err = model.SelfRedis.Do("BLPOP", TcpListChannel, 4).Result()
		if err != nil {
			continue
		}

		dataSlice = redisData.([]interface{})
		err = json.Unmarshal([]byte(dataSlice[1].(string)), &data)
		if err != nil {
			gogo_log.Error("server_TcpWorkByPull", "json解释错误", err.Error())
			return
		}
		content := data.Content
		switch data.CmdMessage {
		case MessageByExit: //退出
			gogo_log.Write("tcpSendMessage", "退出消息", dataSlice[1].(string))
			if data.Content[MessageContentByEquipment] != nil {
				data.toExit(data.Content[MessageContentByEquipment].(string))
			}
			break
		case MessageByLogin: //登录
			gogo_log.Write("tcpSendMessage", "登录消息", dataSlice[1].(string))
			if content[MessageContentByEquipment] != nil && content[MessageContentByLinkServer] != nil && content[MessageContentByPortIp] != nil {
				data.toLogin(content[MessageContentByEquipment].(string), content[MessageContentByLinkServer].(string), content[MessageContentByPortIp].(string))
			}
			break
		default:
			gogo_log.Write("tcpSendMessage", "不存在的消息", dataSlice[1].(string))
		}

		time.Sleep(time.Second)
		data = MessageByStructMain{}
	}
}

//登录
func (ms *MessageByStructMain) toLogin(equipment string, linkServer string, portIp string) {
	//添加设备号,检查设备是否存在,存在进行覆盖,否则进行添加
	tx, err := model.PgSql.Begin()
	if err != nil {
		gogo_log.Error("server_toLogin", "sql事务启动失败", err.Error())
		return
	}
	defer tx.Rollback()
	loginEquipmentSql, args := sql_aid.PgTable(model.TableByUserLoginEquipment).Where("user_id = ?", ms.SendUserID).Where("equipment = ?", equipment).ToSql()
	loginEquipmentData, err := sql_aid.DataToMap(model.PgSql.Query(loginEquipmentSql, args...))
	if err != nil {
		gogo_log.Error("server_toLogin", "查询设备表错误", err.Error())
		return
	}
	if len(loginEquipmentData) == 0 { //空则进行添加
		err = model.UserLoginEquipmentByNewEquipment(tx, ms.SendUserID, equipment, linkServer, portIp)
	} else { //非空则进行更改
		err = model.UserLoginEquipmentByOldEquipment(tx, ms.SendUserID, equipment, linkServer, portIp)
	}
	if err != nil {
		gogo_log.Error("server_toLogin", "处理设备表错误", err.Error())
		return
	}
	//找到用户所有的好友id
	friendSql, args := sql_aid.PgTable(model.TableByChatFriend).Where("user_id = ?", ms.SendUserID).ToSql()
	friendData, err := sql_aid.DataToMap(tx.Query(friendSql, args...))
	if err != nil {
		gogo_log.Error("server_toLogin", "找好友sql查询错误", err.Error())
		return
	}
	friendIds := util.ArrayField(&friendData, "friend_id")
	ms.ReceiveUserIds = append(ms.ReceiveUserIds, friendIds...)  //通知所有的好友
	ms.ReceiveUserIds = append(ms.ReceiveUserIds, ms.SendUserID) //自己的设备也要通知
	//根据好友id获取所有正在登录的设备号
	userLoginEquipmentSql, args := sql_aid.PgTable(model.TableByUserLoginEquipment).Field("link_server").Where("is_online = ?", model.UserLoginEquipmentByIsOnlineYes).Where("is_del = ?", model.UserLoginEquipmentByIsDelNo).WhereIn("user_id", ms.ReceiveUserIds...).GroupBy("link_server").ToSql()
	userLoginEquipmentData, err := sql_aid.DataToMap(tx.Query(userLoginEquipmentSql, args...))
	if err != nil {
		gogo_log.Error("server_toLogin", "根据好友找设备错误", err.Error())
		return
	}
	userLoginEquipmentDataBySlice := util.ArrayField(&userLoginEquipmentData, "link_server")
	if !util.InArray(&userLoginEquipmentDataBySlice, linkServer) { //如果通知里面没有新设备，则进行添加进去
		userLoginEquipmentDataBySlice = append(userLoginEquipmentDataBySlice, linkServer)
	}
	//进行发送
	//转成json字符串发送redis订阅
	userData, err := model.UserByInfo(ms.SendUserID)
	ms.SendUser = userData
	jsonStringData, err := json.Marshal(ms)
	if err != nil {
		return
	}
	//根据设备号给对应的redis订阅发送消息
	for _, v := range userLoginEquipmentDataBySlice {
		_, err = model.SelfRedis.Do("RPUSH", v, string(jsonStringData)).Result()
		if err != nil {
			gogo_log.Error("server_toLogin", "发送到队列失败", err.Error())
			return
		}
	}
	tx.Commit()

}

//用户退出
func (ms *MessageByStructMain) toExit(equipment string) {
	tx, err := model.PgSql.Begin()
	if err != nil {
		gogo_log.Error("server_toExit", "sql事务启动失败", err.Error())
		return
	}
	defer tx.Rollback()
	err = model.UserLoginEquipmentByExit(tx, ms.SendUserID, equipment)
	if err != nil {
		gogo_log.Error("server_toExit", "更改sql错误", err.Error())
		return
	}
	tx.Commit()
}

func (ms *MessageByStructMain) Push() error {
	//查找需要发送的用户设备
	userLoginEquipmentSql, args := sql_aid.PgTable(model.TableByUserLoginEquipment).Field("link_server").Where("is_online = ?", model.UserLoginEquipmentByIsOnlineYes).Where("is_del = ?", model.UserLoginEquipmentByIsDelNo).WhereIn("user_id", ms.ReceiveUserIds...).GroupBy("link_server").ToSql()
	userLoginEquipmentData, err := sql_aid.DataToMap(model.PgSql.Query(userLoginEquipmentSql, args...))
	if err != nil {
		gogo_log.Error("server_toLogin", "根据好友找设备错误", err.Error())
		return err
	}
	redisData, err := json.Marshal(&ms)
	if err != nil {
		return err
	}
	userLoginEquipmentDataBySlice := util.ArrayField(&userLoginEquipmentData, "link_server")
	//根据设备号给对应的redis订阅发送消息
	for _, v := range userLoginEquipmentDataBySlice {
		gogo_log.Write("tcpSendMessage", fmt.Sprintf("向%v服务号发送消息", v), string(redisData))
		_, err = model.SelfRedis.Do("RPUSH", v, string(redisData)).Result()
		if err != nil {
			gogo_log.Error("server_toLogin", "发送到队列失败", fmt.Sprintf("data:%v,err=%v", string(redisData), err.Error()))
		}
	}
	return nil
}
